﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;
using Liquid.Windows.Properties;

namespace Liquid.Windows {
	public partial class TrayApplicationContext : ApplicationContext {

		#region Fields (11)

		static string DefaultApplicationName;
		ContextMenuStrip defaultContextMenu;
		bool exitRequested = false;
		bool formStartupShown = false;
		private bool alwaysShowInTray;
		private string applicationName = DefaultApplicationName;
		private bool minimizeToTray = true;
		private bool showStillRunningMessage = true;
		private bool startInTray;
		NotifyIcon notifyIcon;

		#endregion Fields

		#region Constructors (2)

		/// <summary>
		/// Initializes a new instance of the <see cref="TrayApplicationContext"/> class.
		/// </summary>
		/// <param name="form">The form.</param>
		public TrayApplicationContext(Form form)
			: base(form) {
			base.MainForm = form;
			notifyIcon = new NotifyIcon();

			form.SizeChanged += new EventHandler(form_SizeChanged);
			form.Shown += new EventHandler(form_Shown);
			form.FormClosing += new FormClosingEventHandler(form_FormClosing);
			notifyIcon.Icon = form.Icon;
			notifyIcon.Text = form.Text;
			notifyIcon.MouseClick += new MouseEventHandler(notifyIcon_MouseClick);

			notifyIcon.ContextMenuStrip = defaultContextMenu = new ContextMenuStrip();
			var showItem = defaultContextMenu.Items.Add("&Show", null, delegate { Show(); });
			showItem.Font = new Font(showItem.Font, FontStyle.Bold);
			defaultContextMenu.Items.Add(new ToolStripSeparator());
			defaultContextMenu.Items.Add("E&xit", null, delegate { Exit(); });

			if (form is ITrayForm) {
				ITrayForm trayForm = (ITrayForm)form;
				trayForm.TrayApplicationContext = this;
			}
		}

		static TrayApplicationContext() {
			var assembly = Assembly.GetEntryAssembly();
			var attr = assembly
				.GetCustomAttributes(typeof(AssemblyTitleAttribute), true)
				.Cast<AssemblyTitleAttribute>()
				.DefaultIfEmpty()
				.SingleOrDefault();
			if (attr != null)
				DefaultApplicationName = attr.Title;
			else
				DefaultApplicationName = new AssemblyName(assembly.FullName).Name;
		}

		#endregion Constructors

		#region Properties (8)

		/// <summary>
		/// Gets or sets a value indicating whether [always show in tray].
		/// </summary>
		/// <value><c>true</c> if [always show in tray]; otherwise, <c>false</c>.</value>
		public bool AlwaysShowInTray {
			get { return alwaysShowInTray; }
			set {
				if (!Object.Equals(alwaysShowInTray, value)) {
					alwaysShowInTray = value;
					OnAlwaysShowInTrayChanged(EventArgs.Empty);
				}
			}
		}

		public string ApplicationName {
			get { return applicationName; }
			set { applicationName = value; }
		}

		public bool CloseToTray { get; set; }


		public bool Animate { get; set; }


		public ContextMenuStrip ContextMenuStrip {
			get { return notifyIcon.ContextMenuStrip; }
			set { notifyIcon.ContextMenuStrip = value ?? defaultContextMenu; }
		}

		public Icon Icon {
			get { return notifyIcon.Icon; }
			set { notifyIcon.Icon = value; }
		}

		public bool MinimizeToTray {
			get { return minimizeToTray; }
			set {
				if (!Object.Equals(minimizeToTray, value)) {
					minimizeToTray = value;
					OnMinimizeToTrayChanged(EventArgs.Empty);
				}
			}
		}

		public bool ShowStillRunningMessage {
			get { return showStillRunningMessage; }
			set { showStillRunningMessage = value; }
		}

		public bool StartInTray {
			get { return startInTray; }
			set {
				if (!Object.Equals(startInTray, value)) {
					startInTray = value;
				}
			}
		}

		#endregion Properties

		#region Methods (10)


		// Public Methods (3) 

		/// <summary>
		/// Exits the application.
		/// </summary>
		public void Exit() {
			exitRequested = true;
			MainForm.Close();
		}

		/// <summary>
		/// Hides the application.
		/// </summary>
		public void Hide() {
			MainForm.Hide();
			if (Animate)
				AnimateHide();
			notifyIcon.Visible = true;
			if (ShowStillRunningMessage)
				notifyIcon.ShowBalloonTip(
					100,
					string.Format(Resources.Tray_StillRunningTitle, ApplicationName),
					string.Format(Resources.Tray_StillRunningText, ApplicationName),
					ToolTipIcon.Info
				);
		}

		/// <summary>
		/// Shows the application.
		/// </summary>
		public void Show() {
			if (!MainForm.Visible) {
				if (Animate)
					AnimateShow();
				MainForm.Show();
				if (!AlwaysShowInTray)
					notifyIcon.Visible = false;
			}
			MainForm.WindowState = lastWindowState;
		}


		private void AnimateHide() {
			UnsafeNative.APPBARDATA appBarData = new UnsafeNative.APPBARDATA();
			UnsafeNative.SHAppBarMessage(UnsafeNative.AppBarMessage.ABM_GETTASKBARPOS, ref appBarData);
			var taskBarLocation = appBarData.rc;
			var windowLocation = UnsafeNative.RECT.FromRectangle(MainForm.Bounds);
			UnsafeNative.DrawAnimatedRects(
				MainForm.Handle,
				UnsafeNative.IDANI_CAPTION,
				ref windowLocation,
				ref taskBarLocation
			);
		}

		private void AnimateShow() {
			UnsafeNative.APPBARDATA appBarData = new UnsafeNative.APPBARDATA();
			UnsafeNative.SHAppBarMessage(UnsafeNative.AppBarMessage.ABM_GETTASKBARPOS, ref appBarData);
			var taskBarLocation = appBarData.rc;
			var windowLocation = UnsafeNative.RECT.FromRectangle(MainForm.Bounds);
			UnsafeNative.DrawAnimatedRects(
				MainForm.Handle,
				UnsafeNative.IDANI_CAPTION,
				ref taskBarLocation,
				ref windowLocation
			);
		}

		// Protected Methods (3) 

		protected override void Dispose(bool disposing) {
			notifyIcon.Dispose();
			base.Dispose(disposing);
		}

		protected virtual void OnAlwaysShowInTrayChanged(EventArgs e) {
			if (alwaysShowInTray && !notifyIcon.Visible) {
				notifyIcon.Visible = true;
			} else if (!alwaysShowInTray && notifyIcon.Visible) {
				if (MainForm != null && MainForm.IsHandleCreated)
					notifyIcon.Visible = false;
			}
		}

		protected virtual void OnMinimizeToTrayChanged(EventArgs e) {
			if (MainForm.WindowState == FormWindowState.Minimized)
				if (minimizeToTray)
					Hide();
				else
					Show();
		}



		// Private Methods (4) 

		void form_FormClosing(object sender, FormClosingEventArgs e) {
			if (CloseToTray && !exitRequested)
				if (e.CloseReason == CloseReason.UserClosing) {
					Hide();
					e.Cancel = true;
				}
		}

		void form_Shown(object sender, EventArgs e) {
			if (formStartupShown)
				return;
			formStartupShown = true;
			if (StartInTray)
				Hide();
		}

		FormWindowState lastWindowState;
		void form_SizeChanged(object sender, EventArgs e) {
			if (MainForm.WindowState == FormWindowState.Minimized && MinimizeToTray)
				Hide();
			else
				lastWindowState = MainForm.WindowState;
		}

		void notifyIcon_MouseClick(object sender, MouseEventArgs e) {
			if (e.Button == MouseButtons.Left)
				Show();
		}


		#endregion Methods

	}
}
